import javax.swing.*;

import jpcap.JpcapCaptor;
import jpcap.NetworkInterface;
import jpcap.packet.*;

import java.io.IOException;
import java.net.*;
import java.util.Enumeration;
import java.util.List;

public abstract class CaptureThread{
	
	private jpcap.NetworkInterface[] my_nic2;
	private jpcap.JpcapCaptor capture;
	private jpcap.JpcapSender send_pack;
	private Packet_Builder read_data;
	private Packet packet;
	private ThreadVar threadVar;
	private Object value;
	private utils util = new utils();
	
	public CaptureThread()
	{
		my_nic2 = jpcap.JpcapCaptor.getDeviceList();
		try {
			read_data = new Packet_Builder(packet);
		} catch (UnknownHostException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		
		final Runnable doFinished = new Runnable()
		{
			public void run() { finished(); }
		};
		Runnable doConstruct = new Runnable()
		{
			public void run()
			{
				try
				{
					setValue(construct());
				}finally
				{
					threadVar.clear();
				}
				SwingUtilities.invokeLater(doFinished);
			}
		};
		Thread t = new Thread(doConstruct);
		threadVar = new ThreadVar(t);
	}
	
	/**
	 * Enumeration<NetworkInterface> nic = NetworkInterface.getNetworkInterfaces();		
	 *	my_nic = nic.nextElement().getByIndex(25);
	 *	pr.receivePacket(capture.getPacket());		
	 *	capture.processPacket(1, pr);
	 *
	 * * Class to maintain reference to current worker thread
     * under separate synchronization control.

	 *
	 * @param args
	 * @throws IOException
	 */	
	private static class ThreadVar
	{
		private Thread thread;
		public ThreadVar(Thread t)
		{ 
			thread = t; 
		}
		synchronized Thread get() { return thread; }
		synchronized void clear() { thread = null; }
	}
	
	/** 
     * Set the value produced by worker thread 
     */
	private synchronized void setValue(Object x)
	{
		value = x;
	}
	
	/** 
     * Get the value produced by the worker thread, or null if it 
     * hasn't been constructed yet.
     */
	private synchronized Object getValue()
	{
		return value;
	}
	 /** 
     * Compute the value to be returned by the <code>get</code> method. 
     */

	public abstract Object construct();
	
	/**
     * Called on the event dispatching thread (not on the worker thread)
     * after the <code>construct</code> method has returned.
     */
	public void finished(){ interrupt(); }
	
	 /**
     * Return the value created by the <code>construct</code> method.  
     * Returns null if either the constructing thread or the current
     * thread was interrupted before a value was produced.
     * 
     * @return the value created by the <code>construct</code> method
     */

	public Object get()
	{
		while(true)
		{
			Thread t = threadVar.get();
			if(t == null)
				return getValue();
			
			try
			{
				t.join();
			}catch(InterruptedException e)
			{
				System.out.println(e);
			}
		}
	}
	
	/**
     * A new method that interrupts the worker thread.  Call this method
     * to force the worker to stop what it's doing.
     */
	public void interrupt()
	{
		Thread t = threadVar.get();
		if(t != null)
		{
			t.interrupt();			
		}
		threadVar.clear();
	}
	
	/**
     * Start a thread that will call the <code>construct</code> method
     * and then exit.
     */
	public void start()
	{
		Thread t = threadVar.get();
		if(t !=null)
			t.start();
	}
	public int startcapture(NetworkInterface i)
	{
		jpcap.NetworkInterface nic = i;
		try{
				if(capture==null)
					capture = jpcap.JpcapCaptor.openDevice(nic, -1, false, 1000);
			
				packet = capture.getPacket();
				if(packet != null)
				{
					if(packet.header.length > 0)
					{		
						read_data.setPkt(packet);
						return 1;
					}
				}	
			}catch(Exception e){ System.out.println(e); }
		return 0;		
	}
	
	
	public void sendPacket(NetworkInterface i, String dst_IPorMAC, String protocol_Type,int src_port,int dst_port) throws IOException
	{
		Packet_Builder pb = new Packet_Builder(packet);
		send_pack = jpcap.JpcapSender.openDevice(i);
		EthernetPacket ep = new EthernetPacket();
		ep.src_mac = i.mac_address;
		if(protocol_Type.compareTo("TCP")==0)
		{
			TCPPacket tp = pb.createTCPPacket();			
			ep.dst_mac = util.StringtoMacAddress(dst_IPorMAC);	
			ep.frametype = read_data.ETHERTYPE_IP;	
			tp.datalink = ep;			
			send_pack.sendPacket(tp);	
		}
		if(protocol_Type.compareTo("UDP")==0)
		{
			UDPPacket udp = pb.createUDPPacket(src_port, dst_port, dst_IPorMAC);			
			ep.dst_mac = util.StringtoMacAddress(dst_IPorMAC);	
			ep.frametype = read_data.ETHERTYPE_IP;	
			udp.datalink = ep;
			send_pack.sendPacket(udp);	
		}
		if(protocol_Type.compareTo("ARP")==0)
		{
			ARPPacket arp = pb.createARPPacket(i.mac_address,util.StringtoIPAddress("192.168.1.100"),
					util.StringtoMacAddress("00:00:00:00:00:00"),util.StringtoIPAddress(dst_IPorMAC));				
			ep.dst_mac = util.StringtoMacAddress("ff:ff:ff:ff:ff:ff");
			ep.frametype = read_data.ETHERTYPE_ARP;			
			arp.datalink = ep;
			send_pack.sendPacket(arp);
		}
		send_pack.close();		
	}
	
	
	public jpcap.NetworkInterface[] getMy_nic2() {	return my_nic2;	}
	public void setMy_nic2(jpcap.NetworkInterface[] my_nic2) {	this.my_nic2 = my_nic2;	}
	public Packet_Builder getRead_data() {	return read_data;	}
	public void setRead_data(Packet_Builder read_data) {	this.read_data = read_data;	}
	
}
